package com.jonychen.swagger2demo.common.config;


import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.github.xiaoymin.swaggerbootstrapui.model.OrderExtensions;
import com.github.xiaoymin.swaggerbootstrapui.model.SpringAddtionalModel;
import com.github.xiaoymin.swaggerbootstrapui.service.SpringAddtionalModelService;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.jonychen.swagger2demo.common.config.extend.DeveloperApiInfo;
import com.jonychen.swagger2demo.common.config.extend.DeveloperApiInfoExtension;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.*;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.servlet.http.HttpSession;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import static com.google.common.base.Predicates.or;
import static springfox.documentation.builders.PathSelectors.ant;
import static springfox.documentation.builders.PathSelectors.regex;

/**
 * @Description:swagger配置类
 * @Author: kui.chen@100credit.com
 * @Date: 2019/9/6 14:35
 */
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {

    @Autowired
    SpringAddtionalModelService springAddtionalModelService;

    /**
     * 通过@Configuration注解，让Spring来加载该类配置。再通过@EnableSwagger2注解来启用Swagger2。
     * 再通过createRestApi函数创建Docket的Bean之后，apiInfo()用来创建该Api的基本信息（这些基本信息会展现在文档页面中）。
     * select()函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现，可采用指定扫描的包路径或注解来定义，
     * Swagger会扫描该包下所有Controller定义的API，并产生文档内容（除了被@ApiIgnore指定的请求）
     * @return
     */
    @Bean(value = "defaultApi")
    @Order(value = 1)
    public Docket createRestApi() throws UnknownHostException {

        //扫描，多个包中间用逗号隔开
        SpringAddtionalModel springAddtionalModel= springAddtionalModelService.scan("com.jonychen.swagger2demo.common.config.extend","com.jonychen.swagger2demo.common.entity.model");
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                //是否开启swagger，默认开启
                .enable(true)
                //自定义全局响应消息
                .useDefaultResponseMessages(false)
                .globalResponseMessage(RequestMethod.GET, getGlobalResponseMessageList())
                .globalResponseMessage(RequestMethod.POST, getGlobalResponseMessageList())
                //生成离线文档此处不能打开，离线文档生成会去找它的默认组，踩坑记
                //.groupName("默认接口")
                .select()
                //这里采用包含注解的方式来确定要显示的接口
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                //这里采用包扫描的方式来确定要显示的接口
                //.apis(RequestHandlerSelectors.basePackage("com.jonychen.springbootswaggerdemo.controller"))
                .paths(PathSelectors.any())
                //展现我们想展现的，一种是基于 @ApiIgnore 注解，另一种是在 Docket 上增加筛选
                //自定义展示Api路径,只展示自己想要展示的,此处修改请勿提交svn
                //.paths(myApiPaths())
                //排除以下API路径
                .paths(Predicates.and(
                        Predicates.not(ant("/error")),
                        Predicates.not(ant("/insurancePolicy/**"))
                ))
                .build()
                .additionalModels(springAddtionalModel.getFirst(),springAddtionalModel.getRemaining())
                .ignoredParameterTypes(HttpSession.class).extensions(Lists.newArrayList(new OrderExtensions(2))).securityContexts(Lists.newArrayList(securityContext())).securitySchemes(Lists.<SecurityScheme>newArrayList(apiKey()))
                .globalOperationParameters(getGlobalOperationParameters())
                //设置权限
                .securityContexts(Lists.newArrayList(securityContext())).securitySchemes(Lists.<SecurityScheme>newArrayList(apiKey()));
    }

    private ApiInfo apiInfo() throws UnknownHostException {
        return new ApiInfoBuilder()
                .title("SpringBoot中使用Swagger2构建RESTful API")
                //在线文档可以使用以下样式
                /*.description("<div style='font-size:14px;color:red;'>springboot-swagger-demo API文档</div>")*/
                //生成离线文档需要把样式删除，不然会导致json无法解析，踩坑记
                .description("springboot-swagger-demo API文档")
                .termsOfServiceUrl("http://" + InetAddress.getLocalHost().getHostAddress() + ":8089/swagger-ui.html")
                .contact(setContact())
                .extensions(Lists.newArrayList(setDeveloperContact()))
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("1.0")
                .build();
    }



    /**
     * 展示我们想在swagger上展示的接口
     * @return
     */
    @SuppressWarnings("unchecked")
    private Predicate<String> myApiPaths() {
        return or(
                regex("/user.*"),
                regex("/activity.*"),
                regex("/contacts.*"),
                regex("/pet.*"),
                regex("/springsRestController.*"),
                regex("/test.*")
        );
    }

    /**
     * 自定义全局响应消息
     * @return
     */
    private  List<ResponseMessage>  getGlobalResponseMessageList(){
        List<ResponseMessage> responseMessageList = new ArrayList<>();
        responseMessageList.add(new ResponseMessageBuilder().code(404).message("找不到资源").responseModel(new ModelRef("GlobalApiError")).build());
        responseMessageList.add(new ResponseMessageBuilder().code(409).message("业务逻辑异常").responseModel(new ModelRef("GlobalApiError")).build());
        responseMessageList.add(new ResponseMessageBuilder().code(422).message("参数校验异常").responseModel(new ModelRef("GlobalApiError")).build());
        responseMessageList.add(new ResponseMessageBuilder().code(500).message("服务器内部错误").responseModel(new ModelRef("GlobalApiError")).build());
        responseMessageList.add(new ResponseMessageBuilder().code(503).message("Hystrix异常").responseModel(new ModelRef("GlobalApiError")).build());
        return responseMessageList;
    }

    /**
     * 设置全局操作参数
     */
    private List<Parameter>  getGlobalOperationParameters(){
        ParameterBuilder parameterBuilder=new ParameterBuilder();
        List<Parameter> parameters= Lists.newArrayList();
        parameterBuilder.name("token").description("token令牌").modelRef(new ModelRef("String"))
                .parameterType("header")
                .required(true)
                .build();
        parameters.add(parameterBuilder.build());
        return parameters;
    }

    /**
     * 设置联系人
     * @return
     */
    private Contact setContact(){
        return new Contact("Jonychen","https://www.cnblogs.com/lxcy/","kui.chen@100credit.com");
    }

    /**
     * 设置开发者信息
     * @return
     */
    private DeveloperApiInfoExtension setDeveloperContact(){
        return new DeveloperApiInfoExtension()
                .addDeveloper(new DeveloperApiInfo("Jonychen","kui.chen@100credit.com","Java"))
                .addDeveloper(new DeveloperApiInfo("张三","zhangsan@163.com","Java"))
                .addDeveloper(new DeveloperApiInfo("李四","lisi@163.com","Java"));
    }

    private ApiKey apiKey() {
        return new ApiKey("BearerToken", "Authorization", "header");
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("/.*"))
                .build();
    }

    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Lists.newArrayList(new SecurityReference("BearerToken", authorizationScopes));
    }
}
